package br.inf.mfpo.fra4and.utils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;

import android.content.Context;
import android.text.format.DateFormat;
import br.inf.mfpo.fra4and.R;

/**
 * Utilitários de formatação
 * 
 * @author MarcosFPo
 * 
 */
public class FormatterUtils {

	private FormatterUtils() {
		super();
	}

	public static final int SHOW_DAYS = 2;
	public static final int SHOW_HOURS = 4;
	public static final int SHOW_MINUTES = 8;
	public static final int SHOW_SECONDS = 16;

	/**
	 * Formata data e hora usando o formato padrão do local.<br>
	 * Ex. dd/MM/yyyy HH:mm no BR, MM/dd/yyyy HH:mm nos EUA
	 * 
	 * @param context
	 * @param value
	 * @return
	 */
	public static String formatDateTime(Context context, long value) {
		final java.text.DateFormat dateFormatter = DateFormat.getDateFormat(context);
		final java.text.DateFormat timeFormatter = DateFormat.getTimeFormat(context);

		Date date = new Date(value);
		final String strDate = dateFormatter.format(date);
		final String strTime = timeFormatter.format(date);
		return strDate + " " + strTime;
	}

	/**
	 * Formata data e hora usando o formato padrão do local.<br>
	 * Ex. dd/MM/yyyy HH:mm no BR, MM/dd/yyyy HH:mm nos EUA
	 * 
	 * @param context
	 * @param year
	 * @param month
	 * @param day
	 * @param hour
	 * @param minute
	 * @return
	 */
	public static String formatDateTime(Context context, int year, int month, int day, int hour, int minute) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.MONTH, month);
		cal.set(Calendar.DAY_OF_MONTH, day);
		cal.set(Calendar.HOUR_OF_DAY, hour);
		cal.set(Calendar.MINUTE, minute);
		return formatDateTime(context, cal.getTimeInMillis());
	}

	/**
	 * Formata data usando o formato padrão do local.<br>
	 * Ex. dd/MM/yyyy no BR, MM/dd/yyyy nos EUA
	 * 
	 * @param context
	 * @param value
	 * @return
	 */
	public static String formatDate(Context context, long value) {
		final java.text.DateFormat dateFormatter = DateFormat.getDateFormat(context);

		Date date = new Date(value);
		return dateFormatter.format(date);
	}

	/**
	 * Formata data usando o formato padrão do local.<br>
	 * Ex. dd/MM/yyyy no BR, MM/dd/yyyy nos EUA
	 * 
	 * @param context
	 * @param year
	 * @param month
	 * @param day
	 * @return
	 */
	public static String formatDate(Context context, int year, int month, int day) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.MONTH, month);
		cal.set(Calendar.DAY_OF_MONTH, day);
		return formatDate(context, cal.getTimeInMillis());
	}

	/**
	 * Formata hora usando o formato padrão do local.<br>
	 * Ex. HH:mm
	 * 
	 * @param context
	 * @param value
	 * @return
	 */
	public static String formatTime(Context context, long value) {
		final java.text.DateFormat timeFormatter = DateFormat.getTimeFormat(context);

		Date date = new Date(value);
		return timeFormatter.format(date);
	}

	/**
	 * Formata hora usando o formato padrão do local.<br>
	 * Ex. HH:mm
	 * 
	 * @param context
	 * @param hour
	 * @param minute
	 * @return
	 */
	public static String formatTime(Context context, int hour, int minute) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.HOUR_OF_DAY, hour);
		cal.set(Calendar.MINUTE, minute);
		return formatTime(context, cal.getTimeInMillis());
	}

	/**
	 * Formata diferença entre datas <br>
	 * Saída: 12 dia(s) 03 hora(s) 17 minuto(s) 34 segundo(s)<br>
	 * Requer que os valores da localização sejam alterados no strings.xml
	 * 
	 * @param context
	 * @param begin
	 * @param end
	 * @return
	 */
	public static String formatElapsed(Context context, long begin, long end, int flags) {
		return formatElapsed(context, end - begin, flags);
	}

	/**
	 * Formata tempo decorrido<br>
	 * Saída: 12 dia(s) 03 hora(s) 17 minuto(s) 34 segundo(s)<br>
	 * Requer que os valores da localização sejam alterados no strings.xml
	 * 
	 * @param context
	 * @param elapsed
	 * @return
	 */
	public static String formatElapsed(Context context, long elapsed, int flags) {
		String format = String.format("%%0%dd", 2);

		int days = 0;
		int hours = 0;
		int minutes = 0;
		long seconds = (elapsed / 1000);

		if ((flags & SHOW_DAYS) == SHOW_DAYS) {
			days = (int) Math.floor(seconds / 60 / 60 / 24);
			seconds -= (days * 60 * 60 * 24);
		}
		if ((flags & SHOW_HOURS) == SHOW_HOURS) {
			hours = (int) Math.floor(seconds / 60 / 60);
			seconds -= (hours * 60 * 60);
		}
		if ((flags & SHOW_MINUTES) == SHOW_MINUTES) {
			minutes = (int) Math.floor(seconds / 60);
			seconds -= minutes * 60;
		}
		if ((flags & SHOW_SECONDS) != SHOW_SECONDS) {
			seconds = 0;
		}

		StringBuilder stb = new StringBuilder();

		if (days == 0 && hours == 0 && minutes == 0 && seconds == 0) {
			String nowWord = context.getString(R.string.now);
			stb.append(nowWord);
		} else {
			if (days > 0) {
				String daysWord = context.getString(R.string.days);
				stb.append(String.format(format, days));
				stb.append(" ".concat(daysWord));
				stb.append((hours > 0 || minutes > 0) ? ", " : "");
			}
			if (hours > 0) {
				String hoursWord = context.getString(R.string.hours);
				stb.append(String.format(format, hours));
				stb.append(" ".concat(hoursWord));
				stb.append(minutes > 0 ? ", " : "");
			}
			if (minutes > 0) {
				String minutesWord = context.getString(R.string.minutes);
				stb.append(String.format(format, minutes));
				stb.append(" ".concat(minutesWord));
				stb.append(seconds > 0 ? ", " : "");
			}
			if (seconds > 0) {
				String secondsWord = context.getString(R.string.seconds);
				stb.append(String.format(format, seconds));
				stb.append(" ".concat(secondsWord));
			}
		}

		return stb.toString().trim();
	}

	/**
	 * Formata um número em decimal<br>
	 * Ex.: value=1, fractionDigits=2; formatado=1,00
	 * 
	 * @param value
	 * @param fractionDigits
	 * @return
	 */
	public static String formatDecimal(float value, int fractionDigits) {
		final NumberFormat decimalFormatter = DecimalFormat.getInstance();

		BigDecimal decimal = new BigDecimal(value);
		decimal.setScale(fractionDigits, BigDecimal.ROUND_HALF_UP);
		decimalFormatter.setMinimumFractionDigits(fractionDigits);
		decimalFormatter.setMaximumFractionDigits(fractionDigits);
		return decimalFormatter.format(decimal.doubleValue());
	}

	/**
	 * Formata em moeda levando em conta a localização<br>
	 * Ex.: value=1, fractionDigits=2; formatado= R$ 1,00 (no BR);
	 * 
	 * @param value
	 * @param fractionDigits
	 * @return
	 */
	public static String formatCurrency(float value, int fractionDigits) {
		final NumberFormat currencyFormatter = DecimalFormat.getCurrencyInstance();

		BigDecimal decimal = new BigDecimal(value);
		decimal.setScale(fractionDigits, BigDecimal.ROUND_DOWN);
		currencyFormatter.setMinimumFractionDigits(fractionDigits);
		currencyFormatter.setMaximumFractionDigits(fractionDigits);
		return currencyFormatter.format(decimal.doubleValue());
	}

	/**
	 * Converte uma moeda de String para número
	 * 
	 * @param value
	 * @return
	 * @throws ParseException
	 */
	public static Number parseCurrency(String value) throws ParseException {
		final NumberFormat currencyFormatter = DecimalFormat.getCurrencyInstance();
		return currencyFormatter.parse(value);
	}

	/**
	 * Formata na moeda especificada<br>
	 * Ex.: value=1, fractionDigits=2; formatado= R$ 1,00 (no BR);
	 * 
	 * @param value
	 * @param fractionDigits
	 * @param currency
	 * @return
	 */
	public static String formatCurrency(float value, int fractionDigits, Currency currency) {
		final NumberFormat currencyFormatter = DecimalFormat.getCurrencyInstance();
		currencyFormatter.setCurrency(currency);

		BigDecimal decimal = new BigDecimal(value);
		decimal.setScale(fractionDigits, BigDecimal.ROUND_DOWN);
		currencyFormatter.setMinimumFractionDigits(fractionDigits);
		currencyFormatter.setMaximumFractionDigits(fractionDigits);
		return currencyFormatter.format(decimal.doubleValue());
	}
}
